#pragma once
#ifndef MYDIRECT3DDEVICE_H
#define MYDIRECT3DDEVICE_H

#include "common.h"
#include "d3d.h"
#include "MyDirect3DVertexBuffer7.h"

class MyDirect3DDevice7 : public IDirect3DDevice7 {
public:
    MyDirect3DDevice7(IDirect3D7* direct3D7, IDirect3DDevice7* direct3DDevice7) : direct3D7(direct3D7), direct3DDevice7(direct3DDevice7) {
        DebugWrite("MyDirect3DDevice7::MyDirect3DDevice7\n");
    }

    inline void GetOriginal(IDirect3DDevice7** direct3DDevice7) {
        *direct3DDevice7 = this->direct3DDevice7;
    }

    /*** IUnknown methods ***/
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObj) {
        DebugWrite("MyDirect3DDevice7::QueryInterface\n");
        return this->direct3DDevice7->QueryInterface(riid, ppvObj);
    }

    ULONG STDMETHODCALLTYPE AddRef() {
        DebugWrite("MyDirect3DDevice7::AddRef\n");
        return this->direct3DDevice7->AddRef();
    }

    ULONG STDMETHODCALLTYPE Release() {
        DebugWrite("MyDirect3DDevice7::Release\n");
        ULONG count = this->direct3DDevice7->Release();
        if (0 == count) {
            delete this;
        }

        return count;
    }

    /*** IDirect3DDevice7 methods ***/
    HRESULT STDMETHODCALLTYPE GetCaps(LPD3DDEVICEDESC7 lpD3DDevDesc ) {
        DebugWrite("MyDirect3DDevice7::GetCaps\n");
        return this->direct3DDevice7->GetCaps(lpD3DDevDesc);
    }

    HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index,float* pPlane) {
        DebugWrite("MyDirect3DDevice7::GetClipPlane\n");
        return this->direct3DDevice7->GetClipPlane(Index, pPlane);
    }

    HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD dwIndex, D3DVALUE* pPlaneEquation) {
        DebugWrite("MyDirect3DDevice7::SetClipPlane\n");
        return this->direct3DDevice7->SetClipPlane(dwIndex, pPlaneEquation);
    }

    HRESULT STDMETHODCALLTYPE GetClipStatus(LPD3DCLIPSTATUS lpD3DClipStatus) {
        DebugWrite("MyDirect3DDevice7::GetClipStatus\n");
        return this->direct3DDevice7->GetClipStatus(lpD3DClipStatus);
    }

    HRESULT STDMETHODCALLTYPE SetClipStatus(LPD3DCLIPSTATUS lpD3DClipStatus) {
        DebugWrite("MyDirect3DDevice7::SetClipStatus\n");
        return this->direct3DDevice7->SetClipStatus(lpD3DClipStatus);
    }

    HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D7** ppD3D9) {
        DebugWrite("MyDirect3DDevice7::GetDirect3D\n");
        HRESULT hr = this->direct3DDevice7->GetDirect3D(ppD3D9);
        if (SUCCEEDED(hr)) {
            *ppD3D9 = this->direct3D7;
        }

        return hr;
    }

    HRESULT STDMETHODCALLTYPE GetInfo(DWORD dwDevInfoID, LPVOID pDevInfoStruct, DWORD dwSize) {
        DebugWrite("MyDirect3DDevice7::GetInfo\n");
        return this->direct3DDevice7->GetInfo(dwDevInfoID, pDevInfoStruct, dwSize);
    }

    HRESULT STDMETHODCALLTYPE GetLight(DWORD dwLightIndex, LPD3DLIGHT7 lpLight) {
        DebugWrite("MyDirect3DDevice7::GetLight\n");
        return this->direct3DDevice7->GetLight(dwLightIndex, lpLight);
    }

    HRESULT STDMETHODCALLTYPE SetLight(DWORD dwLightIndex, LPD3DLIGHT7 lpLight) {
        DebugWrite("MyDirect3DDevice7::SetLight\n");
        return this->direct3DDevice7->SetLight(dwLightIndex, lpLight);
    }

    HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index,BOOL* pEnable) {
        DebugWrite("MyDirect3DDevice7::GetLightEnable\n");
        return this->direct3DDevice7->GetLightEnable(Index, pEnable);
    }

    HRESULT STDMETHODCALLTYPE GetMaterial(LPD3DMATERIAL7 lpMaterial) {
        DebugWrite("MyDirect3DDevice7::GetMaterial\n");
        return this->direct3DDevice7->GetMaterial(lpMaterial);
    }

    HRESULT STDMETHODCALLTYPE SetMaterial(LPD3DMATERIAL7 lpMaterial) {
        DebugWrite("MyDirect3DDevice7::SetMaterial\n");
        return this->direct3DDevice7->SetMaterial(lpMaterial);
    }

    HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State,DWORD* pValue) {
        DebugWrite("MyDirect3DDevice7::GetRenderState\n");
        return this->direct3DDevice7->GetRenderState(State, pValue);
    }

    HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State,DWORD Value) {
        DebugWrite("MyDirect3DDevice7::SetRenderState\n");
        return this->direct3DDevice7->SetRenderState(State, Value);
    }

    HRESULT STDMETHODCALLTYPE GetRenderTarget(LPDIRECTDRAWSURFACE7 *lplpRenderTarget ) {
        DebugWrite("MyDirect3DDevice7::GetRenderTarget\n");
        return this->direct3DDevice7->GetRenderTarget(lplpRenderTarget);
    }

    HRESULT STDMETHODCALLTYPE SetRenderTarget(LPDIRECTDRAWSURFACE7 lpNewRenderTarget, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::SetRenderTarget\n");
        return this->direct3DDevice7->SetRenderTarget(lpNewRenderTarget, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE GetTexture(DWORD dwStage, LPDIRECTDRAWSURFACE7 *lplpTexture) {
        DebugWrite("MyDirect3DDevice7::GetTexture\n");
        return this->direct3DDevice7->GetTexture(dwStage, lplpTexture);
    }

    HRESULT STDMETHODCALLTYPE SetTexture(DWORD dwStage, LPDIRECTDRAWSURFACE7 lplpTexture) {
        DebugWrite("MyDirect3DDevice7::SetTexture\n");
        return this->direct3DDevice7->SetTexture(dwStage, lplpTexture);
    }

    HRESULT STDMETHODCALLTYPE GetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD* pValue) {
        DebugWrite("MyDirect3DDevice7::GetTextureStageState\n");
        return this->direct3DDevice7->GetTextureStageState(Stage, Type, pValue);
    }

    HRESULT STDMETHODCALLTYPE SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) {
        DebugWrite("MyDirect3DDevice7::SetTextureStageState\n");
        return this->direct3DDevice7->SetTextureStageState(Stage, Type, Value);
    }

    HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State,D3DMATRIX* pMatrix) {
        DebugWrite("MyDirect3DDevice7::GetTransform\n");
        return this->direct3DDevice7->GetTransform(State, pMatrix);
    }

    HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE dtstTransformStateType, LPD3DMATRIX lpD3DMatrix) {
        DebugWrite("MyDirect3DDevice7::SetTransform\n");
        return this->direct3DDevice7->SetTransform(dtstTransformStateType, lpD3DMatrix);
    }

    HRESULT STDMETHODCALLTYPE GetViewport(LPD3DVIEWPORT7 lpViewport) {
        DebugWrite("MyDirect3DDevice7::GetViewport\n");
        return this->direct3DDevice7->GetViewport(lpViewport);
    }

    HRESULT STDMETHODCALLTYPE SetViewport(LPD3DVIEWPORT7 lpViewport) {
        DebugWrite("MyDirect3DDevice7::SetViewport\n");
        return this->direct3DDevice7->SetViewport(lpViewport);
    }

    HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD dwBlockHandle) {
        DebugWrite("MyDirect3DDevice7::ApplyStateBlock\n");
        return this->direct3DDevice7->ApplyStateBlock(dwBlockHandle);
    }

    HRESULT STDMETHODCALLTYPE BeginScene() {
        DebugWrite("MyDirect3DDevice7::BeginScene\n");
        return this->direct3DDevice7->BeginScene();
    }

    HRESULT STDMETHODCALLTYPE BeginStateBlock() {
        DebugWrite("MyDirect3DDevice7::BeginStateBlock\n");
        return this->direct3DDevice7->BeginStateBlock();
    }

    HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD dwBlockHandle) {
        DebugWrite("MyDirect3DDevice7::CaptureStateBlock\n");
        return this->direct3DDevice7->CaptureStateBlock(dwBlockHandle);
    }

    HRESULT STDMETHODCALLTYPE Clear(DWORD dwCount, LPD3DRECT lpRects, DWORD dwFlags, D3DCOLOR dwColor, D3DVALUE dvZ, DWORD dwStencil) {
        DebugWrite("MyDirect3DDevice7::Clear\n");
        return this->direct3DDevice7->Clear(dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
    }

    HRESULT STDMETHODCALLTYPE ComputeSphereVisibility(LPD3DVECTOR lpCenters, LPD3DVALUE lpRadii, DWORD dwNumSpheres, DWORD dwFlags, LPDWORD lpdwReturnValues) {
        DebugWrite("MyDirect3DDevice7::ComputeSphereVisibility\n");
        return this->direct3DDevice7->ComputeSphereVisibility(lpCenters, lpRadii, dwNumSpheres, dwFlags, lpdwReturnValues);
    }

    HRESULT STDMETHODCALLTYPE CreateStateBlock(D3DSTATEBLOCKTYPE d3dsbType, LPDWORD lpdwBlockHandle) {
        DebugWrite("MyDirect3DDevice7::CreateStateBlock\n");
        return this->direct3DDevice7->CreateStateBlock(d3dsbType, lpdwBlockHandle);
    }

    HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD dwBlockHandle) {
        DebugWrite("MyDirect3DDevice7::DeleteStateBlock\n");
        return this->direct3DDevice7->DeleteStateBlock(dwBlockHandle);
    }

    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive(D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPVOID lpvVertices, DWORD dwVertexCount, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawIndexedPrimitive\n");
        return this->direct3DDevice7->DrawIndexedPrimitive(dptPrimitiveType, dwVertexTypeDesc, lpvVertices, dwVertexCount, lpwIndices, dwIndexCount, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveStrided(D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPD3DDRAWPRIMITIVESTRIDEDDATA lpVertexArray, DWORD dwVertexCount, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawIndexedPrimitiveStrided\n");
        return this->direct3DDevice7->DrawIndexedPrimitiveStrided(dptPrimitiveType, dwVertexTypeDesc, lpVertexArray, dwVertexCount, lpwIndices, dwIndexCount, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveVB(D3DPRIMITIVETYPE d3dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpd3dVertexBuffer, DWORD dwStartVertex, DWORD dwNumVertices, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawIndexedPrimitiveVB\n");
        MyDirect3DVertexBuffer7* buffer = (MyDirect3DVertexBuffer7*)lpd3dVertexBuffer;
        IDirect3DVertexBuffer7* orgBuffer = NULL;
        buffer->GetOriginal(&orgBuffer);
        return this->direct3DDevice7->DrawIndexedPrimitiveVB(d3dptPrimitiveType, orgBuffer, dwStartVertex, dwNumVertices, lpwIndices, dwIndexCount, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE DrawPrimitive(D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPVOID lpvVertices, DWORD dwVertexCount, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawPrimitive\n");
        return this->direct3DDevice7->DrawPrimitive(dptPrimitiveType, dwVertexTypeDesc, lpvVertices, dwVertexCount, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE DrawPrimitiveStrided(D3DPRIMITIVETYPE dptPrimitiveType, DWORD dwVertexTypeDesc, LPD3DDRAWPRIMITIVESTRIDEDDATA lpVertexArray, DWORD dwVertexCount, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawPrimitiveStrided\n");
        return this->direct3DDevice7->DrawPrimitiveStrided(dptPrimitiveType, dwVertexTypeDesc, lpVertexArray, dwVertexCount, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE DrawPrimitiveVB(D3DPRIMITIVETYPE d3dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpd3dVertexBuffer, DWORD dwStartVertex, DWORD dwNumVertices, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::DrawPrimitiveVB\n");
        MyDirect3DVertexBuffer7* buffer = (MyDirect3DVertexBuffer7*)lpd3dVertexBuffer;
        IDirect3DVertexBuffer7* orgBuffer = NULL;
        buffer->GetOriginal(&orgBuffer);
        return this->direct3DDevice7->DrawPrimitiveVB(d3dptPrimitiveType, orgBuffer, dwStartVertex, dwNumVertices, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE EndScene() {
        DebugWrite("MyDirect3DDevice7::EndScene\n");
        return this->direct3DDevice7->EndScene();
    }

    HRESULT STDMETHODCALLTYPE EndStateBlock(LPDWORD lpdwBlockHandle) {
        DebugWrite("MyDirect3DDevice7::EndStateBlock\n");
        return this->direct3DDevice7->EndStateBlock(lpdwBlockHandle);
    }

    HRESULT STDMETHODCALLTYPE EnumTextureFormats(LPD3DENUMPIXELFORMATSCALLBACK lpd3dEnumPixelProc, LPVOID lpArg) {
        DebugWrite("MyDirect3DDevice7::EnumTextureFormats\n");
        return this->direct3DDevice7->EnumTextureFormats(lpd3dEnumPixelProc, lpArg);
    }

    HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index,BOOL Enable) {
        DebugWrite("MyDirect3DDevice7::LightEnable\n");
        return this->direct3DDevice7->LightEnable(Index, Enable);
    }

    HRESULT STDMETHODCALLTYPE Load(LPDIRECTDRAWSURFACE7 lpDestTex, LPPOINT lpDestPoint, LPDIRECTDRAWSURFACE7 lpSrcTex, LPRECT lprcSrcRect, DWORD dwFlags) {
        DebugWrite("MyDirect3DDevice7::Load\n");
        return this->direct3DDevice7->Load(lpDestTex, lpDestPoint, lpSrcTex, lprcSrcRect, dwFlags);
    }

    HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE dtstTransformStateType, LPD3DMATRIX lpD3DMatrix) {
        DebugWrite("MyDirect3DDevice7::MultiplyTransform\n");
        return this->direct3DDevice7->MultiplyTransform(dtstTransformStateType, lpD3DMatrix);
    }

    HRESULT STDMETHODCALLTYPE PreLoad(LPDIRECTDRAWSURFACE7 lpddsTexture) {
        DebugWrite("MyDirect3DDevice7::PreLoad\n");
        return this->direct3DDevice7->PreLoad(lpddsTexture );
    }

    HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses) {
        DebugWrite("MyDirect3DDevice7::ValidateDevice\n");
        return this->direct3DDevice7->ValidateDevice(pNumPasses);
    }

private:
    IDirect3DDevice7* direct3DDevice7;
    IDirect3D7* direct3D7;
};

#endif